#pragma once
#include "Http.h"
#include "Serialization/BufferReader.h"
#include "Serialization/MemoryWriter.h"

DECLARE_DELEGATE_OneParam(FBaseHttpRequestFinishDelegate, int32 /*UniqueID*/);
DECLARE_DELEGATE_FourParams(FBaseHttpRequestCompleteDelegate, int32 /*UniqueID*/, FHttpRequestPtr /*Request*/, FHttpResponsePtr /*Response*/, bool /*bConnectedSuccessfully*/);
DECLARE_DELEGATE_FourParams(FBaseHttpRequestHeaderReceivedDelegate, int32 /*UniqueID*/, FHttpRequestPtr /*Request*/, const FString& /*HeaderName*/, const FString& /*NewHeaderValue*/);
DECLARE_DELEGATE_FourParams(FBaseHttpRequestProgressDelegate, int32 /*UniqueID*/, FHttpRequestPtr /*Request*/, int32 /*BytesSent*/, int32 /*BytesReceived*/);
DECLARE_DELEGATE_FourParams(FBaseHttpRequestWillRetryDelegate, int32 /*UniqueID*/, FHttpRequestPtr /*Request*/, FHttpResponsePtr /*Response*/, float /*SecondsToRetry*/);

#ifdef ENABLE_DETAIL_EASY_HTTP
#define RecordDetailLog(format, ...) \
	HttpLogWarning("%s", *(FDateTime::Now().ToString()+FString::Printf(TEXT(",Frame:%lu"), GFrameNumber)+TEXT(",Function:")+ANSI_TO_TCHAR(__FUNCTION__)+TEXT(",")+FString::Printf(TEXT(format), ##__VA_ARGS__)+TEXT(".\n")))

#define RecordDetailLogf(Valid, format, ...) \
if (!(Valid)) { RecordDetailLog(format, ##__VA_ARGS__); }

#else
#define RecordDetailLog(format, ...)
#define RecordDetailLogf(Valid, format, ...) 
#endif

namespace EHttpRequestStage
{
	enum Type
	{
		eStage_None,
		eStage_Initialized,
		eStage_Processing,
		eStage_Failed,
		eStage_OutOfDate,
		eStage_Succeed,
	};

	const TCHAR* ToString(const Type eStage);
}

class FBaseHttpRequest
{
public:
	FBaseHttpRequest();
	virtual ~FBaseHttpRequest();

public:
	void BaseInitilize(int32 UniqueID, const FString &Url);

public:
	virtual bool NeedDeserialized() const;

	virtual void Serialized(FMemoryWriter &Writer);
	virtual void Deserialized(FBufferReader &Reader);

public:
	int32 GetID() const { return m_nUinqueID; }

	bool IsValid() const { return m_pRequest.IsValid(); }

	virtual bool IsFailed() const { return m_eStage == EHttpRequestStage::eStage_Failed; }
	virtual bool IsSucceed() const { return m_eStage == EHttpRequestStage::eStage_Succeed; }
	virtual bool IsOutOfDate() const { return m_eStage == EHttpRequestStage::eStage_OutOfDate; }

	bool IsFinishRequest() const { return IsFailed() || IsSucceed() || IsOutOfDate(); };

	EHttpRequestStage::Type GetStage() const { return m_eStage; }
	const TCHAR* GetStageStr() const { return EHttpRequestStage::ToString(m_eStage); }

public:
	virtual bool DoRequest();
	virtual void Cancel();
	virtual bool Retry();

	virtual bool CanRetry() const;
	virtual int32 GetRecvBytes() const;

	virtual const TArray<uint8>* GetData() const;

	virtual void ResetRequest();

protected:
	virtual void CreateRequest();
	virtual void DestroyRequest();

	virtual void InitHeader();
	virtual void BindAllEvents();
	virtual void UnbindAllEvents();
	virtual void UpdateStage(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bConnectedSuccessfully);

	bool IsResponseSucceed(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bConnectedSuccessfully) const;

public:
	FBaseHttpRequestFinishDelegate FinishDelegate;
	FBaseHttpRequestCompleteDelegate CompleteDelegate;
	FBaseHttpRequestHeaderReceivedDelegate HeaderReceivedDelegate;
	FBaseHttpRequestProgressDelegate ProgressDelegate;
	FBaseHttpRequestWillRetryDelegate WillRetryDelegate;

public:
	FString GetVerb() const;
	void SetVerb(const FString& Verb);

	void SetURL(const FString& URL);
	void SetContent(const TArray<uint8>& ContentPayload);

	void SetContentAsString(const FString& ContentString);
	bool SetContentAsStreamedFile(const FString& Filename);
	bool SetContentFromStream(TSharedRef<FArchive, ESPMode::ThreadSafe> Stream);

	void SetHeader(const FString& HeaderName, const FString& HeaderValue);
	void AppendToHeader(const FString& HeaderName, const FString& AdditionalHeaderValue);

	bool ProcessRequest();
	void CancelRequest();

	EHttpRequestStatus::Type GetStatus() const;

	const FHttpResponsePtr GetResponse() const;

	float GetElapsedTime() const;

protected:
	virtual FString GatherCompleteMsg(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bConnectedSuccessfully);

protected:
	virtual void CompleteCallback(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bConnectedSuccessfully);
	virtual void HeaderReceivedCallback(FHttpRequestPtr Request, const FString& HeaderName, const FString& NewHeaderValue);
	virtual void ProgressCallback(FHttpRequestPtr Request, int32 BytesSent, int32 BytesReceived);
	virtual void WillRetryCallback(FHttpRequestPtr Request, FHttpResponsePtr Response, float SecondsToRetry);


private:
	void CompleteCallbackInner(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bConnectedSuccessfully);
	void HeaderReceivedCallbackInner(FHttpRequestPtr Request, const FString& HeaderName, const FString& NewHeaderValue);
	void ProgressCallbackInner(FHttpRequestPtr Request, int32 BytesSent, int32 BytesReceived);
	void WillRetryCallbackInner(FHttpRequestPtr Request, FHttpResponsePtr Response, float SecondsToRetry);

protected:
	FString m_Url;
	int32 m_nUinqueID;
	int32 m_LasetRequest;
	EHttpRequestStage::Type m_eStage;
	TSharedPtr<IHttpRequest> m_pRequest;
};
